package rtda

import (
	"gitee.com/microwww/jvm-go/jvm/class"
	"gitee.com/microwww/jvm-go/jvm/java"

	"fmt"
)

type Thread struct {
	loader     *class.ClassLoader
	pc         int          // 从 classfile 文件中读取的 offset 是 `uint16` 类型
	stack      *MethodStack // 每个方法开始执行 都需要初始化一个 stack
	javaThread *class.Object
}

type MethodStack struct {
	max  int
	head *MethodFrame
}

type MethodFrame struct {
	parent   *MethodFrame    // 方法的调用栈的 `链表`
	thread   *Thread         // 当前线程, 被 push 到线程的时候被初始化
	stack    *OperationStack // 操作数栈
	localVar LocalVar        // 方法内部的本地变量列表, 数组类型
	nextPC   int             // 指令是根据编译的`.classfile`文件中 代码的字节码的位置指定的
	method   *class.Method
}

type LocalVar struct {
	java.Slots
}

func newLocalVar(max uint16) LocalVar {
	return LocalVar{java.NewLocalVar(max)}
}

type OperationStack struct {
	*java.Stack
}

func (o *OperationStack) String() string {
	return fmt.Sprintf("%v", o.Stack)
}

func NewThread(loader *class.ClassLoader) *Thread {
	return &Thread{
		loader: loader,
		stack:  NewMethodStack(),
	}
}

func (t *Thread) JavaThread() *class.Object {
	if t.javaThread == nil {
		c := t.loader.DefineClass("java/lang/Thread")
		t.javaThread = class.NewObject(c)
	}
	return t.javaThread
}

func (t *Thread) PC() int {
	return t.pc
}

func (t *Thread) SetPC(v int) {
	t.pc = v
}

func (t *Thread) CurrentMethodFrame() *MethodFrame {
	return t.stack.head
}

func (t *Thread) PushMethodFrame(v *MethodFrame) {
	v.parent = t.stack.head
	t.stack.head = v
	v.thread = t
}

func (t *Thread) PopMethodFrame() *MethodFrame {
	top := t.stack.head
	if top == nil {
		panic("Method stack is EMPTY")
	}
	t.stack.head = top.parent
	return top
}

// -----------------

func NewMethodStack() *MethodStack {
	return &MethodStack{
		max:  1024,
		head: nil,
	}
}

/////////////////

func NewOperationStack(max uint16) *OperationStack {
	return &OperationStack{java.NewStack(max)}
}

func NewMethodFrame(method *class.Method) *MethodFrame {
	return &MethodFrame{
		stack:    NewOperationStack(method.MaxStack()),
		localVar: newLocalVar(method.MaxLocals()),
		method:   method,
	}
}

func (m *MethodFrame) Thread() *Thread {
	return m.thread
}

func (m *MethodFrame) Method() *class.Method {
	return m.method
}

func (m *MethodFrame) LocalVar() *LocalVar {
	return &m.localVar
}

func (m *MethodFrame) GetThis() *class.Object {
	return m.localVar.GetRef(0)
}

func (m *MethodFrame) OperationStack() *OperationStack {
	return m.stack
}

func (m *MethodFrame) SetNextPC(npc int) {
	m.nextPC = npc
}

func (m *MethodFrame) NextPC() int {
	return m.nextPC
}
